fix: optional canvas-confetti via CDN with cached promise (Fix 2)#409
Open
Niboshi-Wasabi wants to merge 1244 commits intokoala73:mainfrom
Open
fix: optional canvas-confetti via CDN with cached promise (Fix 2)#409Niboshi-Wasabi wants to merge 1244 commits intokoala73:mainfrom
Niboshi-Wasabi wants to merge 1244 commits intokoala73:mainfrom
Conversation
- Replace MIT with AGPL-3.0-only to enforce attribution on derivatives - Move hardcoded Sentry DSN to VITE_SENTRY_DSN env var - Add null-coalesce guards for i18n legend keys and SVG viewBox - Extend Sentry ignoreErrors with 7 additional noise patterns
## Summary Fixes a css transparency issue when selecting languages. Hi! First time contributing here. Found this app and thought it was really cool. I realized it was a github repo and thought I'd try and commit some fixes that I find. If y'all need me to update this PR in any way let me know. ## Type of change - [x] Bug fix - [ ] New feature - [ ] New data source / feed - [ ] New map layer - [ ] Refactor / code cleanup - [ ] Documentation - [ ] CI / Build / Infrastructure ## Affected areas - [ ] Map / Globe - [ ] News panels / RSS feeds - [ ] AI Insights / World Brief - [ ] Market Radar / Crypto - [ ] Desktop app (Tauri) - [ ] API endpoints (`/api/*`) - [ ] Config / Settings - [x] Other: <!-- Language dropdown--> ## Checklist - [x] Tested on [worldmonitor.app](https://worldmonitor.app) variant - [ ] Tested on [tech.worldmonitor.app](https://tech.worldmonitor.app) variant (if applicable) - [ ] New RSS feed domains added to `api/rss-proxy.js` allowlist (if adding feeds) - [x] No API keys or secrets committed - [x] TypeScript compiles without errors (`npm run typecheck`) ## Screenshots Before: <img width="1470" height="803" alt="Screenshot 2026-02-18 at 7 00 25 PM" src="https://github.com/user-attachments/assets/f0e2b8ce-58dc-42f0-a73f-76b7a539fecf" /> After: <img width="1470" height="800" alt="Screenshot 2026-02-18 at 6 59 54 PM" src="https://github.com/user-attachments/assets/a528fc36-45db-40d7-8463-49fb25d07438" />
…lick menus macOS WKWebView shows native Lookup/Translate/Copy menu on right-click, overriding the custom "Hide Intelligence Findings" context menu. Prevents default contextmenu on non-input elements in Tauri only.
Closes koala73#114. On ultra-wide monitors, the map floats left (60% width, 65vh) and panels flow in an L-shape: to the right and below the map. Uses display:contents on panels-grid so individual panels become flow children, wrapping naturally around the CSS float. No JS changes.
…riants WORLDMONITOR-28: Twitter in-app browser (iPad/iOS) injects CONFIG variable. Existing filter used literal apostrophe which may miss Safari's U+2019. Changed Can't → Can.t to match any apostrophe character. Closes WORLDMONITOR-28
- Comprehensive README update: live webcams, ultra-wide layout, Linux AppImage, theme system, auto-updater, error tracking, responsive layout, virtual scrolling, 13 languages, and 8 new roadmap items - Sentry triage: WORLDMONITOR-28 noise filter broadened for smart quotes - Ultra-wide layout: CSS float L-shape for 2000px+ screens (koala73#114) - Version bump: 2.4.0 → 2.4.1
- fixed incorrect spacing on diagram boxes and arrows - formatted the tables everything stays same, no visual changes
- WORLDMONITOR-2A: filter AbortError from fetch abort on navigation - WORLDMONITOR-29: broaden maplibre _layers null crash pattern in beforeSend - WORLDMONITOR-Q: filter stale dynamic import module errors (post-deploy 404s)
Follow llmstxt.org standard so LLMs can understand the project. Concise version (~5KB) and extended version (~21KB) with full data layers, sources, and architecture details.
Add Turkish (tr) as the 14th supported language with full translation of all 1,134 locale keys, locale loader, tr-TR formatting, and flag mapping. Update documentation to reflect 14 languages.
Move pattern from beforeSend extension-only check to ignoreErrors array. All 16 events were iOS Safari with no stack trace — stale cached assets after deploys, not actionable bugs.
…ions - Add i18n keys for all 9 strategic posture theater names (Iran Theater, Baltic Theater, Taiwan Strait, etc.) across all 14 languages - Wire StrategicPosturePanel to use t() for theater display names with fallback to English if key is missing - Include webcam region button translations (ALL, MIDEAST, EUROPE, etc.) that were missing from deployed locale files Fixes koala73#121
- CIIPanel: use existing t('components.cii.noSignals') for empty state
- CountryBriefPage: localize level badge and trend indicator labels
- ETFFlowsPanel: localize summary labels, table headers, net direction
- LiveWebcamsPanel: localize region filter tab labels
- MacroSignalsPanel: localize verdict, signal names, and bullish count
- StrategicRiskPanel: localize score levels, trend label, and trend values
Add Ollama as the primary summarization provider for desktop builds, sitting before Groq/OpenRouter in the fallback chain. This enables fully local, unlimited LLM inference via Ollama's OpenAI-compatible endpoint (/v1/chat/completions). Changes across six layers: - runtime-config: OLLAMA_API_URL + OLLAMA_MODEL secret keys, aiOllama feature toggle (default on), URL validation - sidecar: allowlist + endpoint probe validation (tries /v1/models then /api/tags) - api/ollama-summarize.js: new handler mirroring Groq/OpenRouter with shared Redis cache keys - summarization.ts: tryOllama() + updated chain order in normal, beta, and translation paths (Ollama → Groq → OpenRouter → Browser T5) - RuntimeConfigPanel: signup URLs + i18n help text for new keys - desktop-readiness: aiOllama in key-backed features + readiness check https://claude.ai/code/session_01AGg9fG6LZ8Y6XhvLszdfeY
…ayers Three test files covering Ollama integration: api/ollama-summarize.test.mjs (9 tests): - Fallback signal when unconfigured, on API error, on empty response - Success path with correct provider label and response shape - Model selection via OLLAMA_MODEL env / default fallback - Network error handling (ECONNREFUSED) - Translate mode prompt verification tests/summarization-chain.test.mjs (7 tests): - Ollama success short-circuits chain (Groq never called) - Ollama fail → Groq success fallback - Full fallback when both unconfigured - Provider label correctness for Ollama and Groq - Uniform response shape across providers - Identical fallback signal shapes src-tauri/sidecar/local-api-server.test.mjs (8 new tests): - OLLAMA_API_URL and OLLAMA_MODEL accepted via env-update allowlist - Unknown keys rejected (403) - Validation via /v1/models probe (reachable mock) - Validation via /api/tags native fallback - OLLAMA_MODEL pass-through validation - Non-http protocol rejection (422) - Auth-required behavior preserved with token https://claude.ai/code/session_01AGg9fG6LZ8Y6XhvLszdfeY
Server-side: extract shared CORS, validation, caching, prompt building, and response shaping into api/_summarize-handler.js factory. Each endpoint (Groq, OpenRouter, Ollama) becomes a thin wrapper calling createSummarizeHandler() with a provider config: credentials, API URL, model, headers, and provider label. Client-side: replace three near-identical tryOllama/tryGroq/tryOpenRouter functions with a single tryApiProvider() driven by an API_PROVIDERS config array. Add runApiChain() helper that loops the chain with progress callbacks. Simplify translateText() from three copy-pasted blocks to a single loop over the same provider array. - groq-summarize.js: 297 → 30 lines - openrouter-summarize.js: 295 → 33 lines - ollama-summarize.js: 289 → 34 lines - summarization.ts: 336 → 239 lines - New _summarize-handler.js: 315 lines (shared) - Net: -566 lines of duplication removed Adding a new LLM provider now requires only a provider config object in the endpoint file + one entry in the API_PROVIDERS array. Tests: 13 new tests for the shared factory (cache key, dedup, handler creation, fallback, error casing, HTTP methods). All 42 existing tests pass unchanged. https://claude.ai/code/session_01AGg9fG6LZ8Y6XhvLszdfeY
- Use for...of entries() instead of index-based loops in summarization.ts to satisfy strict noUncheckedIndexedAccess (7 TS18048/TS2345 errors) - Replace fragile API_PROVIDERS[1] with .find(p => p.name === groq) - Add OLLAMA_API_URL and OLLAMA_MODEL to SUPPORTED_SECRET_KEYS in main.rs so keychain secrets are injected into sidecar on desktop startup
…and Ollama UX - Split settings window into 3 tabs: LLMs (Ollama/Groq/OpenRouter), API Keys (data feeds), and Debug & Logs - Add featureFilter option to RuntimeConfigPanel for rendering subsets - Consolidate keychain to single JSON vault entry (1 macOS prompt vs 20) - Add Ollama model discovery with /api/tags + /v1/models fallback - Strip <think> reasoning tokens from Ollama responses - Suppress thinking with think:false in Ollama request body - Parallel secret verification with 15s global timeout - Fix manual model input overlapping dropdown (CSS grid-area + hidden-input class) - Add loading spinners to settings tab panels - Suppress notification popups when settings window is open - Filter embed models from Ollama dropdown - Fix settings window black screen flash with inline dark background
Add local LLM support mentions across feature descriptions, talking points, screenshot suggestions, and changelog. New dedicated section for Ollama/LM Studio as feature koala73#11.
DeckGLMap: guard updateLayers/debounce/raf against null maplibreMap, null out references in destroy() to prevent post-destroy setProps crash. main.ts: filter contentWindow.postMessage (Facebook WebView), vertex shader compile (GPU driver), objectStoreNames (iOS background), Unexpected identifier https (Safari 16), _0x obfuscated vars (extensions), WKWebView deallocated (Tauri lifecycle).
…ule (koala73#373) Switch all 4 WTO trade endpoints from manual getCachedJson/setCachedJson to cachedFetchJson, which coalesces concurrent cache misses into a single upstream WTO API call. This prevents hammering the WTO API when multiple requests arrive during a cache miss window. Also register tradePolicy with the RefreshScheduler at a 10-minute interval (full/finance variants) — previously it was only fetched once at startup with no periodic refresh. https://claude.ai/code/session_01FdvFhpLALL9iJaF8iXMjtu Co-authored-by: Claude <noreply@anthropic.com>
…la73#376) wtoFetch() silently returned null on missing key, HTTP errors, and exceptions — making it impossible to diagnose why trade data shows "temporarily unavailable". Now logs the specific failure reason.
- Remove "Bound Rate" column from tariffs table — WTO TP_B_0090 bound rate data unavailable on our subscription tier (always 0.0%) - Fix restrictions status labels: backend returns high/moderate/low but panel expected IN_FORCE/TERMINATED — now matches actual values - Update all 17 locale files with new tariff level labels
…73#384) Two bugs: 1. Single shared circuit breaker cached one FRED series response for all 7 series, causing identical wrong values across indicators. Now uses per-series breakers. 2. Tab click listeners were destroyed by setContent() debounce. Replaced with event delegation on the stable content element.
…oala73#385) - Suppress "WTO data temporarily unavailable" banner when cached data exists on the active tab — only show when truly empty + upstream down - Add all missing CSS for trade policy panel (228 lines) — cards with hover states, color-coded status badges, structured tariff table, trade flow metrics layout, source links, and warning banner styling
* fix: use cachedFetchJson for theater posture to prevent Wingbits API stampede
The theater posture handler used manual getCachedJson/setCachedJson which
has no request coalescing. During the window between a cache miss and the
cache write completing (~15s Wingbits timeout), every concurrent request
bypassed the cache and fired its own POST to Wingbits with 9 bounding
boxes — causing ~1500 requests/min.
Switch to cachedFetchJson which deduplicates concurrent in-flight
requests via a shared promise, so only one upstream fetch runs at a time.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: migrate all handlers to cachedFetchJson, fix metadata labeling with cachedFetchJsonWithMeta
- Migrate 48 server handlers from manual getCachedJson/setCachedJson to
cachedFetchJson, eliminating cache stampede vulnerability across the
entire codebase (concurrent cache misses now coalesce into one fetch)
- Add cachedFetchJsonWithMeta to redis.ts — returns { data, source }
atomically, fixing TOCTOU race between separate cache check and fetch
- Wire summarize-article.ts and get-usni-fleet-report.ts to use
cachedFetchJsonWithMeta for correct provider/cached metadata
- Add try/catch to 7 handlers that lost graceful degradation
- Add 4 tests for WithMeta source labeling: cache hit, fresh miss,
coalesced followers, and TOCTOU regression protection
* chore: trigger PR sync
---------
Co-authored-by: Mikael Sundberg <mikael.sundberg82@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add Supply Chain Disruption Intelligence service Add new supply_chain domain with 3 RPCs: - GetShippingRates: FRED Baltic Dry Index with spike detection - GetChokepointStatus: NGA nav warnings + AIS vessel aggregation for 6 global chokepoints with composite disruption scoring - GetCriticalMinerals: HHI concentration analysis for 7 strategic minerals from USGS 2024 production data Frontend: 3-tab SupplyChainPanel with event delegation, sparkline SVG, escapeHtml XSS hardening, and circuit breakers per RPC. 13 unit tests for pure scoring functions (_scoring.mjs). * fix: chokepoint upstream detection, FRED weekly frequency, missing CSS P1: Chokepoint handler propagates upstreamUnavailable when maritime sources fail or return empty, instead of always reporting false. P1: FRED BDI uses frequency=w for true 52-week window. P2: Add CSS for sc-status-dot, sc-dot-*, sc-risk-* classes. * fix: allow partial chokepoint data when one upstream source succeeds
…koala73#375) * feat: dynamic sidecar port with EADDRINUSE fallback Rust probes port 46123 via TcpListener::bind; if busy, binds port 0 for an OS-assigned ephemeral port. The actual port is stored in LocalApiState, passed to sidecar via LOCAL_API_PORT env, and exposed to frontend via get_local_api_port IPC command. Frontend resolves the port lazily on first API call (with retry-on-failure semantics) and caches it. All hardcoded 46123 references replaced with dynamic getApiBaseUrl()/getLocalApiPort() accessors. CSP connect-src broadened to http://127.0.0.1:* (frame-src unchanged). * fix: scope CSP to desktop builds and eliminate port TOCTOU race P1: Remove http://127.0.0.1:* from index.html (web build CSP). The wildcard allowed web app JS to probe arbitrary localhost services. Vite's htmlVariantPlugin now injects localhost CSP only when VITE_DESKTOP_RUNTIME=1 (desktop builds). P2: Replace Rust probe_available_port() (bind→release→spawn race) with a confirmed port handshake. Sidecar now handles EADDRINUSE fallback internally and writes the actual bound port to a file. Rust polls the port file (up to 5s) to store only the confirmed port. * fix: isSafeUrl ReferenceError — addresses scoped inside try block `let addresses = []` was declared inside the outer `try` block but referenced after the `catch` on line 200. `let` is block-scoped so every request through isSafeUrl crashed with: ReferenceError: addresses is not defined Move the declaration before the `try` so it's in scope for the return.
…sabled (koala73#390) mlWorker.init() was called unconditionally in App.init(), ignoring the browserModel setting. This caused ONNX model downloads, 120s embed timeouts, and clustering warnings even with the toggle OFF. - Gate mlWorker.init() on browserModel setting (or desktop runtime) - Subscribe to setting changes for dynamic init/terminate on toggle - Replace mlWorker.init() in sentiment-gate with isAvailable check - Clean up subscription in App.destroy() to prevent HMR listener leaks
…ature gate (koala73#391) - Replace non-existent BDIY (Baltic Dry Index) FRED series with real public series: PCU483111483111 (Deep Sea Freight PPI) and TSIFRGHT (Freight Transportation Index). BDIY is proprietary Baltic Exchange data not available on FRED — all shipping requests were failing. - Make "upstream unavailable" banner per-tab instead of global — shipping failure no longer shows the warning on chokepoints and minerals tabs that have valid data. - Remove inconsistent isFeatureAvailable('supplyChain') gate from frontend fetchShippingRates() — server handler already returns empty when FRED_API_KEY is missing, and chokepoints/minerals had no gate.
… filters (koala73#393) PR koala73#382 accidentally removed the maplibregl.FilterSpecification type cast, causing tsc to infer (string | string[])[] which doesn't satisfy setFilter's parameter type. This broke the Vercel build.
…#392) - Add to tech variant security feed group - Add to main variant global feeds as type: 'cyber' - Add www.ransomware.live to RSS proxy allowlist - Set source tier to 3 (specialty)
Runs npm run typecheck (tsc --noEmit) on every PR to catch type errors before merge. Would have prevented the koala73#382 regression.
…73#395) Linux AppImage (koala73#370, koala73#257): - Upgrade CI from Ubuntu 22.04 to 24.04 (GLib 2.80 fixes g_task_set_static_name symbol mismatch) - Set GDK_BACKEND=wayland,x11 when WAYLAND_DISPLAY detected (fixes GTK init on niri/river) - Disable WebKit bubblewrap sandbox in AppImage context (FUSE mount breaks it → blank screen) - Set GTK_IM_MODULE to built-in simple context in AppImage (prevents host module conflicts) RSS proxy: - Add 32 missing domains to allowlist (EuroNews lang variants, international news feeds) - Normalize www prefix in domain validation (prevents entire class of www/non-www mismatch 403s) Console cleanup: - TrendingKeywords: console.log → console.debug for suppressed terms (30+ noisy log lines) - PostHog: add ui_host for reverse proxy setup (fixes /ingest/ 404s)
…73#396) Add a "Streaming" section to the GENERAL settings tab with a quality dropdown (Auto / Low 360p / Medium 480p / High / HD 720p). The setting persists to localStorage and applies to all live streams: - LiveWebcamsPanel: appends vq= to direct embeds, passes through proxy - LiveNewsPanel: sets quality via YT.Player API onReady + desktop proxy - YouTube embed proxy: accepts vq param, calls setPlaybackQuality() Closes koala73#365
…koala73#400) * fix: sort tariff datapoints newest-first in trade policy panel * fix: update tests broken by cachedFetchJson migration - Restore "Strip unterminated" comment in summarize-article.ts that tests use to locate the unterminated tag stripping section - Update ACLED tests to check for cachedFetchJson instead of removed getCachedJson/setCachedJson patterns
…ala73#399) * fix: sort tariff datapoints newest-first in trade policy panel * fix: update tests broken by cachedFetchJson migration - Restore "Strip unterminated" comment in summarize-article.ts that tests use to locate the unterminated tag stripping section - Update ACLED tests to check for cachedFetchJson instead of removed getCachedJson/setCachedJson patterns * fix: sort supply chain chokepoints by disruption score descending
* fix: sort tariff datapoints newest-first in trade policy panel * fix: update tests broken by cachedFetchJson migration - Restore "Strip unterminated" comment in summarize-article.ts that tests use to locate the unterminated tag stripping section - Update ACLED tests to check for cachedFetchJson instead of removed getCachedJson/setCachedJson patterns * chore: bump version to 2.5.9 and make pre-push hook executable * docs: update README with supply chain intel, universal CII, Happy Monitor, security hardening, and recent features
…oise filters (koala73#402) - CSS.escape() on data-news-id querySelector to prevent SyntaxError when news item IDs contain special characters (WORLDMONITOR-5J) - typeof guard on this.player.destroy() for partially-initialized YouTube players (WORLDMONITOR-5C/5B) - 11 new ignoreErrors patterns for IndexedDB races, browser vendor internals, and extension-injected errors - beforeSend filter for blob: URL extension frames
…7 locales (koala73#403) The header.settings i18n key was incorrectly set to "PANELS" (and its translations) for the settings modal title. The modal contains General, Panels, and Sources tabs — the overall title should be "SETTINGS".
…3#404) * fix: sequential Yahoo calls, sector fallback, and missing try-catch guards - list-commodity-quotes: replace Promise.all with fetchYahooQuotesBatch to prevent Yahoo 429 - get-sector-summary: add Yahoo Finance fallback when FINNHUB_API_KEY is missing - list-etf-flows: sequential fetch loop + add missing try-catch around cachedFetchJson - get-macro-signals: replace unnecessary Promise.allSettled([single]) with direct await * fix: tighten AI summary prompts to 2 sentences / 60 words max Summaries were often verbose walls of text despite "2-3 sentences" in the prompt. Changed to "2 concise sentences MAX (under 60 words total)" across all brief, analysis, and fallback prompts. Reduced max_tokens from 150 to 100 to hard-cap output length.
…uards (koala73#406) - list-commodity-quotes: replace Promise.all with fetchYahooQuotesBatch to prevent Yahoo 429 - get-sector-summary: add Yahoo Finance fallback when FINNHUB_API_KEY is missing - list-etf-flows: sequential fetch loop + add missing try-catch around cachedFetchJson - get-macro-signals: replace unnecessary Promise.allSettled([single]) with direct await
…sistence (koala73#407) - Show rate-limited message instead of generic "Failed to load" on Markets, ETF, Commodities, and Sector panels when Yahoo returns 429 - fetchYahooQuotesBatch returns rateLimited flag; early-exit after 3 misses - ETF panel skips retry loop when rate-limited, shows specific i18n message - Fallback Finnhub symbols through Yahoo when API key missing - 401-retry in runtime fetch patch for stale sidecar token after restart - diagFetch auth helper for settings window diagnostic endpoints - Verbose toggle writes to writable dataDir instead of read-only app bundle
* chore: bump v2.5.10 and update README for recent fixes Version 2.5.9 → 2.5.10. Roadmap entries for: - Yahoo Finance rate-limit UX across all market panels - Sidecar auth resilience (401-retry, settings diagFetch) - Verbose toggle persistence to writable data directory - Finnhub-to-Yahoo fallback routing * chore: add v2.5.10 changelog entry
- Load confetti from CDN at runtime when package not in node_modules - Module-scoped cached promise so only one script tag is appended (record type calls run() twice 300ms apart) - No optimizeDeps for canvas-confetti; package stays optional - PR rationale and description in docs/PR_FIX2_CANVAS_CONFETTI_BODY.md Made-with: Cursor
|
@Niboshi-Wasabi is attempting to deploy a commit to the Elie Team on Vercel. A member of the Team first needs to authorize it. |
Contributor
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Makes the celebration service work when the
canvas-confettipackage is not installed innode_modules, so the app builds and runs without "Failed to resolve import 'canvas-confetti'" (e.g. after a fresh clone beforenpm install, or in environments where dependencies are not fully installed).When the import fails: The error occurs when Vite tries to resolve
canvas-confettiat build or dev startup and the package is missing fromnode_modules— for example right after cloning the repo without runningnpm install, or in a workspace where dependencies have not been installed yet. We did not observe it in CI, Tauri build, or a specific Node version; it was the "package not installed" case.Implementation:
import 'canvas-confetti'in source, so Vite never tries to resolve it.<script>tag is ever appended. The record celebration callsrun()twice (300ms apart); without the cache, a second script would be appended if the first load had not finished. All callers now share the same promise.optimizeDeps.include: ['canvas-confetti']in vite.config, so the package remains optional and Vite does not try to pre-bundle it at dev startup.Trade-off: If the CDN is unavailable or the user is offline, celebrations still run but no confetti is shown. When the package is installed (after
npm install), the CDN is used the same way so behavior is consistent.Type of change
Affected areas
/api/*)src/services/celebration.ts)Checklist
api/rss-proxy.jsallowlist (if adding feeds)npm run typecheck)Screenshots
None. Manual check: run the app without
canvas-confettiin node_modules (e.g. remove it from node_modules and runnpm run dev); app should start and celebrations should load confetti from CDN when triggered.